# -*- coding: utf-8 -*-
import sys, math
sys.path.append("../../quidditas")

import quidditas

from components import *

class MovementProcessor(quidditas.Processor):
    """ Update entities that are moving through the world and
    make sure they don't leave the world. """
    component_types = (PositionComponent, VelocityComponent)

    def __init__(self, size):
        """ Create a MovementProcessor for the given world size. """
        super(MovementProcessor, self).__init__()
        self.world_size = size

    def update_entity(self, time, entity, components):
        """ Move a single entity. """
        width = self.world_size[0]
        height = self.world_size[1]
        comp = components[PositionComponent]
        velocity = components[VelocityComponent]

        comp.x += velocity.current_x * time / 1000.0
        if comp.x < 0:
            comp.x = width
        elif comp.x > width:
            comp.x = 0

        comp.y += velocity.current_y * time / 1000.0
        if comp.y < 0:
            comp.y = height
        elif comp.y > height:
            comp.y = 0

class RenderProcessor(quidditas.Processor):
    """ Renders all visible entities. """
    component_types = (PositionComponent, RenderingComponent)

    def __init__(self, screen, images):
        """ Create a new renderprocessor that renders to screen and
        uses images as the image source. """
        super(RenderProcessor, self).__init__()
        self.screen = screen
        self.images = images
        self.super_update = super(RenderProcessor, self).update

    def update(self, time):
        """ This method is overridden to clear the screen and render the world background. """
        self.screen.fill((0,0,0))
        self.super_update(time)

    def update_entity(self, time, entity, components):
        """ Render a single entity. """
        pos = components[PositionComponent]
        render = components[RenderingComponent]
        self.screen.blit(self.images[render.image], (pos.x, pos.y))

class GameController(quidditas.Processor):
    """ Controls the state of the game and kicks of the first wave. """

    def __init__(self, quidditas):
        """ Start the game flow and load the first world. """
        super(GameController, self).__init__()
        self.quidditas = quidditas

        e = quidditas.create_entity("world-A")
        route = e.get_component(RouteDefinitionComponent)
        route_id_counter = 0
        for route_i in route.routes:
            route_entity = quidditas.create_entity("route")
            route_component = route_entity.get_component(RouteComponent)
            route_component.start = route_i[0]
            route_component.end = route_i[1]
            route_component.route_id = route_id_counter
            route_id_counter += 1

    def refresh_entity(self, e):
        pass

class SpawnProcessor(quidditas.Processor):
    """ Process spawn points for enemies. """
    component_types = (PositionComponent, SpawnComponent)

    def update_entity(self, time, entity, components):
        """ See if any of the active spawn points has waited long enough to spawn something.
        Kill the spawn points that have run out. """
        spawn = components[SpawnComponent]
        spawn.current_timer -= time
        if spawn.current_timer <= 0:
            self.spawn_entity(spawn, components[PositionComponent])
            pass
            spawn.current_timer = spawn.rate
            spawn.amount -= 1
        if spawn.amount == 0:
            print "Spawn has run out."
            entity.kill()

    def spawn_entity(self, spawn, position):
        """ Spawn an enemy entity at the given position using the spawn definition. """
        spawned = self.quidditas.create_entity(spawn.type)
        new_position = spawned.get_component(PositionComponent)
        new_position.x = position.x
        new_position.y = position.y

        spawned.get_component(HealthComponent).hp *= spawn.hp_multiplicator

        # Find the route entity for the route specifed in the spawner
        route_entities = self.component_manager.get_entities_with_component(RouteComponent)
        route_entity = None
        for entity_id in route_entities:
            entity = self.quidditas.get_entity(entity_id)
            if entity.get_component(RouteComponent).route_id == spawn.route_id:
                route_entity = entity
                break
        if not route_entity:
            print "No route with id#{} found!".format(spawn.route_id)
            return

        spawned.get_component(PathFinderComponent).route = route_entity

class WaveProcessor(quidditas.Processor):
    """ Places spawners to send waves of enemies into the field. """
    component_types = (WorldGridComponent, RouteDefinitionComponent, WaveDefinitionComponent)

    def __init__(self):
        super(WaveProcessor, self).__init__()
        self.current_wave = 0
        self.send_next_wave = True

    def update_entity(self, time, entity, components):
        """ There should only be one entity: the current world. """
        if self.send_next_wave:
            self.send_next_wave = False
            routes = components[RouteDefinitionComponent]
            for wave in components[WaveDefinitionComponent].waves:
                spawner = self.quidditas.create_entity("spawn-point")
                pos = spawner.get_component(PositionComponent)
                pos.x = routes[wave[4]][0][0][0]
                pos.y = routes[wave[4]][0][1][1]

                spawn = spawner.get_component(SpawnComponent)
                spawn.type = wave[0]
                spawn.amount = wave[1]
                spawn.rate = wave[2]
                spawn.hp_multiplicator = wave[3]
                spawn.route_id = wave[4]
                spawn.current_timer = spawn.rate

class PathfindingProcessor(quidditas.Processor):
    """ Processes an entity thinking about where to go and determines the direction it moves to. """
    component_types = (VelocityComponent, PathFinderComponent, PositionComponent)

    def update_entity(self, time, entity, components):
        """ Check if the entity has a current target and set its movement direction accordingly. """
        pos = components[PositionComponent]
        pathfinder = components[PathFinderComponent]
        velocity = components[VelocityComponent]

        # Center of target cell
        if pathfinder.target is None:
            route = pathfinder.route.get_component(RouteComponent)
            pathfinder.target = route.cell_order[0]
        target_center_x = pathfinder.target[0] * 50 - 25
        target_center_y = pathfinder.target[1] * 50 - 25

        # Check if the entity is within 1.5 units to its target
        if math.fabs(pos.x-target_center_x) <= 1.5 and math.fabs(pos.y-target_center_y) <= 1.5:
            print "finding next cell, current target is:", pathfinder.target
            route = pathfinder.route.get_component(RouteComponent)
            index = route.cell_order.index(pathfinder.target)
            pathfinder.target = route.cell_order[index+1]

        # Calculate direction in which to move
        diff_x = pos.x - target_center_x
        diff_y = pos.y - target_center_y
        if diff_x < -1:
             velocity.current_x = velocity.max_speed
             velocity.current_y = 0
        elif diff_x > 1:
             velocity.current_x = -velocity.max_speed
             velocity.current_y = 0
        elif diff_y < -1:
             velocity.current_y = velocity.max_speed
             velocity.current_x = 0
        elif diff_y > 1:
             velocity.current_y = -velocity.max_speed
             velocity.current_x = 0

class RoutePathfindingProcessor(quidditas.Processor):
    """ Updates the cell_order of all RouteComponents so they contain a valid route from
    start to target. """
    component_types = (RouteComponent)

    def update_entity(self, time, entity, components):
        """ Check if a recalculation is necessary and perform one. """
        # Check if the world has changed since the last update, making a new
        # calculation necessary. Skip calculation when it's not needed
        # TODO: world dirty check
        route = components[RouteComponent]
        route.cell_order = self.calc_route(route.start, route.end)

    def calc_route(self, start, end):
        """ Calculate a new route.
        Currently this does nothing intelligent and just creates a path to the target without checking
        for obstacles. """
        result = []
        for x in range(start[0], end[0]):
            result.append((x, start[1]))
        for y in range(start[1], end[1]):
            result.append((end[0], y))
        result.append(end)
        return result

class EntityReachedItsTargetProcessor(quidditas.Processor):
    """ Checks for all wandering entities when they have reached their goal. """
    component_types = (PathFinderComponent, PositionComponent)